////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_SCRIPTING_INTEROP_HELPERS_H__
#define __WN_SCRIPTING_INTEROP_HELPERS_H__

#include "WNCore/inc/WNAssert.h"
#include "WNScripting/inc/WNScriptingConfig.h"

#if defined _WN_MSVC && _WN_MSVC < 160000000
    #pragma warning(push)
    #pragma warning(disable: 4985)
#endif

#include <math.h>

#if defined _WN_MSVC && _WN_MSVC < 160000000
    #pragma warning(pop)
#endif

namespace WNScripting {
    namespace WNScriptInterop {
        extern WN_BYTE* GetStackArrayReturnLocation(WN_SIZE_T _size);

        ///Note all script array COPIES share the same memory This is memory that is generally allocated for the 
        ///scripting engine
        template<typename Type> class ScriptArray {
        public:
            static WN_SIZE_T GetAdditionalElements() {
                return(static_cast<WN_SIZE_T>(ceill(static_cast<WN_FLOAT32>(sizeof(WNS_INT_CONST)) / static_cast<WN_FLOAT32>(sizeof(Type)))));
            }

            ScriptArray(const ScriptArray<Type>& _other):
                mArrayData(_other.mArrayData),
                mLength(_other.mLength),
                mStartOffset(_other.mStartOffset) {
            }

            ScriptArray(WN_BYTE* _array) {
               mArrayData = reinterpret_cast<Type*>(_array);
               mStartOffset = static_cast<WN_SIZE_T>(ceill(static_cast<WN_FLOAT32>(sizeof(WNS_INT_CONST)) / static_cast<WN_FLOAT32>(sizeof(Type))));
               mLength = *(reinterpret_cast<WNS_INT_CONST*>(mArrayData));
            }

            ScriptArray(WN_BYTE* _array, WN_SIZE_T _initialSize) {
                mArrayData = reinterpret_cast<Type*>(_array);
                mLength = _initialSize;
                mStartOffset = static_cast<WN_SIZE_T>(ceill(static_cast<WN_FLOAT32>(sizeof(WNS_INT_CONST)) / static_cast<WN_FLOAT32>(sizeof(Type))));
                *(reinterpret_cast<WNS_INT_CONST*>(mArrayData)) = static_cast<WNS_INT_CONST>(_initialSize);
            }

            WN_SIZE_T Length() {
                return(mLength);
            }

            Type& operator[] (WN_SIZE_T val) {
                WN_DEBUG_ASSERT(val < mLength);
                return(mArrayData[mStartOffset + val]);
            }
    
            WN_BYTE* GetRawData() {
                return(reinterpret_cast<WN_BYTE*>(mArrayData));
            }

            WN_UINT32 GetSizeInBytes() {
                return(static_cast<WN_UINT32>(sizeof(Type) * (mLength + mStartOffset)));
            }

        private:
            Type* mArrayData;
            WN_SIZE_T mLength;
            WN_SIZE_T mStartOffset;
        };

        template<typename Type> ScriptArray<Type> CreateScriptArrayForStack(WN_SIZE_T _size) {
            WN_SIZE_T stackSize = _size + ScriptArray<Type>::GetAdditionalElements();
            WN_BYTE* loc = GetStackArrayReturnLocation(stackSize);

            return(ScriptArray<Type>(loc, _size));
        }
    }
}
#endif // __WN_SCRIPTING_INTEROP_HELPERS_H__
